﻿// Author: Wilfred Pinto
// Website: http://projectsilverlight.blogspot.com

using SimpleViewer_SL.code;
using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Resources;

namespace SimpleViewer_SL
{
    /// <summary>
    /// Implements the Photo UserControl
    /// </summary>
    /// <event cref="PropertyChangedEventHandler">Property Changed Event</event>
    public partial class Photo : UserControl, INotifyPropertyChanged, ISimpleViewerSLControl
    {
        #region INotifyPropertyChanged Members

        /// <inheritdoc />
        public event PropertyChangedEventHandler PropertyChanged;
        
        #endregion

        #region ISimpleViewerSLControl Members

        /// <inheritdoc />
        public double ControlWidth { get { return (_parent.Settings.MaxImageWidth + (_parent.Settings.FrameWidth * 2)); } }

        /// <inheritdoc />
        public double ControlHeight { get { return (_parent.Settings.MaxImageHeight + (_parent.Settings.FrameWidth * 2)); } }

        #endregion

        Page _parent;
        int _selectedPhotoIndex = -1;
        bool _leftArrowClicked = false;
        bool _rightArrowClicked = false;

        readonly TimeSpan AnimationDuration = TimeSpan.FromSeconds(0.2);

        /// <param name="parent">The <c>Page</c> which instantiates this object</param>
        public Photo(Page parent)
        {
            // Save the parent so that we can access it later
            _parent = parent;

            InitializeComponent();

            // Initialize some variables/objects
            NavLeftPath.Fill = new SolidColorBrush(_parent.Settings.FrameColor);
            NavRightPath.Fill = new SolidColorBrush(_parent.Settings.FrameColor);

            NavLeftShowStoryboard.Children[0].Duration = NavLeftHideStoryboard.Children[0].Duration = AnimationDuration;
            NavRightShowStoryboard.Children[0].Duration = NavRightHideStoryboard.Children[0].Duration = AnimationDuration;

            // Capture the MouseLeftButtonDown event of the layout 
            // to expand the scope of the left and right navigation buttons
            LayoutRoot.MouseLeftButtonDown += (sender, e) =>
            {
                if (e.GetPosition(LayoutPhoto).X < (LayoutPhoto.ActualWidth / 2) - 10)
                {
                    // Clicking on the left half of the photo is
                    // equivalent to clicking the left navigation button
                    _leftArrowClicked = true;

                    // Show the previous photo
                    SelectedPhotoIndex -= 1;
                }
                else if (e.GetPosition(LayoutPhoto).X > (LayoutPhoto.ActualWidth / 2) + 10)
                {
                    // Clicking on the right half of the photo is
                    // equivalent to clicking the right navigation button
                    _rightArrowClicked = true;

                    // Show the next photo
                    SelectedPhotoIndex += 1;
                }
            };

            // Capture the MouseMove event of the layout 
            // to show or fade out the navigation buttons 
            // depending on where the mouse is moved on the image
            LayoutRoot.MouseMove += (sender, e) =>
            {
                double X = e.GetPosition(LayoutPhoto).X;

                _leftArrowClicked = false;
                _rightArrowClicked = false;

                if (NavLeft.Opacity < 0.1 && X < (LayoutPhoto.ActualWidth / 2) - 10)
                {
                    ShowLeftNav();
                }
                else if (NavLeft.Opacity > 0 && X >= (LayoutPhoto.ActualWidth / 2) - 10)
                {
                    NavLeftHideStoryboard.Begin();
                }

                if (NavRight.Opacity < 0.1 && X > (LayoutPhoto.ActualWidth / 2) + 10)
                {
                    ShowRightNav();
                }
                else if (NavRight.Opacity > 0 && X <= (LayoutPhoto.ActualWidth / 2) + 10)
                {
                    NavRightHideStoryboard.Begin();
                }
            };

            // Capture the MouseLeave event of the layout to fade out
            // the navigations button when the mouse leaves the image
            LayoutRoot.MouseLeave += (sender, e) =>
            {
                NavLeftHideStoryboard.Begin();
                NavRightHideStoryboard.Begin();
            };
        }

        /// <summary>
        /// Gets or sets the selected photo index and displays the selected photo
        /// </summary>
        public int SelectedPhotoIndex
        {
            get { return _selectedPhotoIndex; }
            set
            {
                // Check if value is within bounds and not already selected
                if (value >= 0 && value < _parent.Settings.ImagesSettings.Count && value != _selectedPhotoIndex)
                {
                    _selectedPhotoIndex = value;

                    // Call NotifyPropertyChanged when the source property is updated
                    NotifyPropertyChanged("SelectedPhotoIndex");

                    // Show the newly selected photo
                    ShowPhoto(_selectedPhotoIndex);
                }
            }
        }


        /// <summary>
        /// NotifyPropertyChanged will fire the PropertyChanged event passing the
        /// source property that is being updated.
        /// </summary>
        /// <param name="propertyName">The source property to pass to the event</param>
        /// <example><code>NotifyPropertyChanged("SelectedPhotoIndex");</code></example>
        /// <remarks>Only <c>"SelectedPhotoIndex"</c> is passed currently</remarks>
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// Draws a border around the image
        /// </summary>
        /// <param name="imageBorder">The border to be manipulated</param>
        /// <param name="imageSize">The size of the image that will have the border around it</param>
        protected void DrawBorderAroundImage(Border imageBorder, Size imageSize)
        {
            // Calculate the width and height for the border
            imageBorder.Width = imageSize.Width + (_parent.Settings.FrameWidth * 2);
            imageBorder.Height = imageSize.Height + (_parent.Settings.FrameWidth * 2);

            if (LayoutPhoto.Children.Count == 1)
            {
                GeneralTransform gt = ((Image)((Border)LayoutPhoto.Children[0]).Child).TransformToVisual(LayoutRoot);
                Point p = gt.Transform(new Point(0, 0));
                NavLeft.Margin = NavRight.Margin = new Thickness(p.X - 1);
            }
        }

        /// <summary>
        /// Displays the specified Photo
        /// </summary>
        /// <remarks>The old photo is faded out and the new photo is faded in</remarks>
        /// <param name="photoIndex">The index of the photo to display</param>
        protected void ShowPhoto(int photoIndex)
        {
            if (photoIndex >= 0)
            {
                try
                {
                    while (LayoutPhoto.Children.Count > 1)
                    {
                        // Remove all photos except the currently displayed photo
                        // There should never be more than 1 photo but this logic
                        // has been added to capture some unexpected condition
                        // especially when the user scroll through the thumbnails
                        // very fast
                        LayoutPhoto.Children.RemoveAt(LayoutPhoto.Children.Count - 1);
                    }

                    // Create an empty image, and empty border to go with the image
                    Image image = new Image
                    {
                        MaxWidth = _parent.Settings.MaxImageWidth - (_parent.Settings.FrameWidth * 2),
                        MaxHeight = _parent.Settings.MaxImageHeight - (_parent.Settings.FrameWidth * 2)
                    };
                    Border border = new Border
                    {
                        BorderThickness = new Thickness(_parent.Settings.FrameWidth),
                        BorderBrush = new SolidColorBrush(_parent.Settings.FrameColor),
                        Margin = new Thickness(_parent.Settings.StagePadding),
                        Opacity = 0
                    };

                    // Apply the border to the image
                    border.Child = image;

                    // Set the source property of the image control
                    image.Source = new BitmapImage(new Uri(_parent.Settings.ImagePath + _parent.Settings.ImagesSettings[photoIndex].Name, UriKind.Relative));

                    // Add the border to the layout.
                    // The image gets added automatically since it is a child of the border
                    LayoutPhoto.Children.Insert(0, border);

                    // We are capturing the SizeChanged event of the image
                    // to reliably get the size of the image
                    image.SizeChanged += (sender, e) =>
                    {
                        if (e.PreviousSize.Width == 0 && e.PreviousSize.Height == 0 && e.NewSize != e.PreviousSize)
                        {
                            // e.NewSize contains the dimension of the image
                            // MyImage.Width and MyImage.ActualWidth is unreliable so use e.NewSize instead
                            DrawBorderAroundImage(border, e.NewSize);

                            // Show the new image by transitioning out the old
                            // and transitioning in the new
                            TransitionImage();
                        }
                    };
                }
                catch (Exception)
                {
                    //	HtmlPage.Window.Alert(e.Message);
                }
            }
        }

        /// <summary>
        /// Fades out the old image and fade in the new image
        /// </summary>
        protected void TransitionImage()
        {
            if (LayoutPhoto.Children.Count > 1)
            {
                // Create a storyboard for the image (old) to be faded out
                Storyboard TransitionImageHide = new Storyboard();

                // Animation to fade out the image
                DoubleAnimation animation = new DoubleAnimation();
                Storyboard.SetTarget(animation, LayoutPhoto.Children[1]);
                Storyboard.SetTargetProperty(animation, new PropertyPath("Opacity", new object[0]));
                animation.To = 0;
                animation.Duration = AnimationDuration;
                TransitionImageHide.Children.Add(animation);
                Resources.Remove("transitionImageHide");
                Resources.Add("transitionImageHide", TransitionImageHide);

                // Start the storyboard to hide the old image 
                TransitionImageHide.Begin();

                // Hide the left and right navigation arrows as well since they
                // are dependant on the position and size of the new image 
                NavLeftHideStoryboard.Begin();
                NavRightHideStoryboard.Begin();

                // Fade in the new image only after the old image is completely faded out
                TransitionImageHide.Completed += (sender, e) =>
                {
                    if (LayoutPhoto.Children.Count > 1)
                    {
                        // Remove the faded out image (old) from the layout
                        LayoutPhoto.Children.RemoveAt(1);
                    }

                    // Create a storyboard for the image (new) to be faded in
                    Storyboard TransitionImageShow = new Storyboard();

                    // Animation to fade in the image
                    DoubleAnimation animation2 = new DoubleAnimation();
                    Storyboard.SetTarget(animation2, LayoutPhoto.Children[0]);
                    Storyboard.SetTargetProperty(animation2, new PropertyPath("Opacity", new object[0]));
                    animation2.To = 1;
                    animation2.Duration = AnimationDuration;
                    TransitionImageShow.Children.Add(animation2);
                    Resources.Remove("transitionImageShow");
                    Resources.Add("transitionImageShow", TransitionImageShow);

                    // Calcuate the position for the left and right navigation buttons
                    GeneralTransform gt = ((Image)((Border)LayoutPhoto.Children[0]).Child).TransformToVisual(LayoutRoot);
                    Point p = gt.Transform(new Point(0, 0));
                    NavLeft.Margin = NavRight.Margin = new Thickness(p.X - 1);

                    // Start the storyboard to show the new image
                    TransitionImageShow.Begin();

                    // Show the left and/or the right navigation buttons if required
                    if (_leftArrowClicked && CanLeftNav()) NavLeftShowStoryboard.Begin();
                    if (_rightArrowClicked && CanRightNav()) NavRightShowStoryboard.Begin();
                };
            }
            else if (LayoutPhoto.Children.Count > 0)
            {
                // Just display the new image without any animation
                LayoutPhoto.Children[0].Opacity = 1;
            }
        }

        /// <summary>
        /// Returns <c>true</c> if the left navigation button can be displayed
        /// </summary>
        /// <returns><c>true</c> - if left navigation button can be displayed<br/>
        /// <c>false</c> - otherwise</returns>
        protected bool CanLeftNav()
        {
            return SelectedPhotoIndex > 0;
        }

        /// <summary>
        /// Returns <c>true</c> if the right navigation button can be displayed
        /// </summary>
        /// <returns><c>true</c> - if right navigation button can be displayed<br/>
        /// <c>false</c> - otherwise</returns>
        protected bool CanRightNav()
        {
            return SelectedPhotoIndex < _parent.Settings.ImagesSettings.Count - 1;
        }

        /// <summary>
        /// Displays the left navigation button
        /// </summary>
        protected void ShowLeftNav()
        {
            if (CanLeftNav())
            {
                NavLeftShowStoryboard.Begin();
            }
        }

        /// <summary>
        /// Displays the right navigation button
        /// </summary>
        protected void ShowRightNav()
        {
            if (CanRightNav())
            {
                NavRightShowStoryboard.Begin();
            }
        }
    }
}